#include "calc.h" /* Contains definition of ’symrec’. */
/* The symbol table: a chain of ’struct symrec’. */
symrec *sym_table;

struct init
{
char const *fname;
void *fnct;
char const *proto;
};
#include <math.h>
struct init const arith_fncts[] =
{
{ "atan", atan, "%lf" },
{ "cos", cos, "%lf" },
{ "exp", exp, "%lf" },
{ "ln", log, "%lf" },
{ "sin", sin, "%lf" },
{ "sqrt", sqrt, "%lf" },
{ "hypot", hypot, "%lf,%lf" },
{ 0, 0, 0 },
};

#include <stdlib.h> /* malloc. */
#include <string.h> /* strlen. */
#include "calc.tab.h" // yyparse, FNCT, VAR
/* Put arithmetic functions in table. */
static
void
init_table (void)
{
int i;
for (i = 0; arith_fncts[i].fname != 0; i++)
{
symrec *ptr = putsym (arith_fncts[i].fname, FNCT);
ptr->value.fnct.fnctptr = arith_fncts[i].fnct;
ptr->value.fnct.proto = (char *) malloc( strlen(arith_fncts[i].proto)+1 );
strcpy(ptr->value.fnct.proto, arith_fncts[i].proto);
}
}

// Handles the symbol table
symrec *
putsym (char const *sym_name, int sym_type)
{
symrec *ptr = (symrec *) malloc (sizeof (symrec));
ptr->name = (char *) malloc (strlen (sym_name) + 1);
strcpy (ptr->name,sym_name);
ptr->type = sym_type;
ptr->value.var = 0; /* Set value to 0 even if fctn. */
ptr->next = (struct symrec *)sym_table;
sym_table = ptr;
return ptr;
}
symrec *
getsym (char const *sym_name)
{
symrec *ptr;
for (ptr = sym_table; ptr != (symrec *) 0;
ptr = (symrec *)ptr->next)
if (strcmp (ptr->name, sym_name) == 0)
return ptr;
return 0;
}
// Arglst handling
int check_arglst(symrec *fnctrec, arglst_t *arglst);
arglst_t *create_arglst(double arg) {
  arglst_t * arglst = (arglst_t *)malloc(sizeof(arglst_t));
  arglst->arg = arg;
  arglst->next = 0;
  return arglst;
}

arglst_t *append_arg(arglst_t *arglst, double arg) {
  arglst_t *ret = arglst;
  while(arglst->next!=0) {
    arglst = arglst->next;
  }
  arglst->next = create_arglst(arg);
  return ret;
}

void discard_arglst(arglst_t *arglst) {
  while(arglst!=0) {
    arglst_t *prev = arglst;
    arglst = arglst->next;
    free(prev);
  }
}

fnctres fnctcall(symrec *fnctrec, arglst_t *arglst) {
  static char const * ERR_FNCTARG = "Bad arguments in call to function";
  fnctres res;
  switch(check_arglst(fnctrec, arglst)) {
    case 1:
    res.stat = 0;   // Status Ok
    res.val = ((double(*)(double))fnctrec->value.fnct.fnctptr)(arglst->arg);  
    return res;
    case 2:
    res.stat = 0;   // Status Ok
    res.val =  ((double(*)(double, double))fnctrec->value.fnct.fnctptr)(arglst->arg, arglst->next->arg);
    return res;
  }
  char * s = (char *) malloc(strlen(ERR_FNCTARG)+strlen(fnctrec->name)+2);
  strcpy(s, ERR_FNCTARG);
  strcat(s, " ");
  strcat(s, fnctrec->name);
  yyerror(s);
  discard_arglst(arglst);
  res.stat = -1;
  res.val = 0;
  return res;
}

int check_arglst(symrec *fnctrec, arglst_t *arglst) {
  int nargs=0;
  char *s = fnctrec->value.fnct.proto;
  size_t length=strcspn(s, ",");
  while(length==3 && strncmp(s, "%lf", length)==0 && arglst!=0 ) {
    nargs++;
    s += length+1;
    length = strcspn(s, ",");
    arglst = arglst->next;
  }
  if(length!=0 || arglst!=0)
    return -1;
  return nargs;
}
#include <stdio.h>
/* Called by yyparse on error. */
void
yyerror (char const *s)
{
fprintf (stderr, "%s\n", s);
}
int
main (int argc, char const* argv[])
{
int i;
/* Enable parse traces on option -p. */
for (i = 1; i < argc; ++i)
if (!strcmp(argv[i], "-p"))
yydebug = 1;
init_table ();
return yyparse ();
}
